﻿using Hangfire;
using Hangfire.Common;
using LJ.TaskScheduler.Hangfire.Pack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace LJ.TaskScheduler.Hangfire;

public class HangfireTaskSchedulerProvider(ITaskSchedulerHangfirePackBuilder taskSchedulerHangfirePackBuilder) : ITaskSchedulerProvider
{
    private readonly ITaskSchedulerHangfirePackBuilder _taskSchedulerHangfirePackBuilder = taskSchedulerHangfirePackBuilder;
    private readonly RecurringJobManager _recurringJobManager = new ();

    public void Register(string name) 
    {
        var workerInfo = _taskSchedulerHangfirePackBuilder.GetWorkerInfos().Where(a => a.Name == name).FirstOrDefault();
        if (workerInfo == null) 
        {
            return;
        }
        Register(workerInfo.Name, workerInfo.Type.GetMethod(nameof(ITaskSchedulerWorker.ExecuteAsync))!, workerInfo.CronExpression, TimeZoneInfo.Local,"LJ");
    }

    public void Remove(string name)
    {
        var workerInfo = _taskSchedulerHangfirePackBuilder.GetWorkerInfos().Where(a => a.Name == name).FirstOrDefault();
        if (workerInfo == null)
        {
            return;
        }
        _recurringJobManager.RemoveIfExists(name);
    }

    private void Register(string recurringJobId, MethodInfo method, string cron, TimeZoneInfo timeZone, string queue)
    {
        var parameters = method.GetParameters();

        Expression[] args = new Expression[parameters.Length];

        for (int i = 0; i < parameters.Length; i++)
        {
            args[i] = Expression.Default(parameters[i].ParameterType);
        }
        var x = Expression.Parameter(method.DeclaringType!, "x");

        var methodCall = method.IsStatic ? Expression.Call(method, args) : Expression.Call(x, method, args);

        var addOrUpdate = Expression.Call(
            typeof(RecurringJobManager),
            nameof(RecurringJobManager.AddOrUpdate),
            [method.DeclaringType!],
            [
                    Expression.Constant(recurringJobId),
                    Expression.Lambda(methodCall, x),
                    Expression.Constant(cron),
                    Expression.Constant(timeZone),
                    Expression.Constant(queue)
            ]);

        Expression.Lambda(addOrUpdate).Compile().DynamicInvoke(_recurringJobManager);
    }
}
